from google.appengine.ext import db
import datetime

class Version(db.Expando):
  """ A previous version of db.Model instance we can revert to
  
  >>> class VersionTestObject(db.Expando):
  ...   name = db.StringProperty()
  >>> t = VersionTestObject(name="Test1", a="Something")
  >>> t.put()
  
  Store a versioned copy
  >>> dt = (lambda *args : datetime.datetime(2009,1,1))
  >>> v = Version.make(t, dt)
  >>> v.put()
  
  Make changes
  >>> t.name = "Test2"
  >>> t.a = "Something Else"
  >>> t.put()
  
  Restore
  >>> v = Version.get(v.key())
  >>> t = v.restore()
  >>> t.name
  'Test1'
  >>> t.a
  'Something'
  
  Clean up
  >>> v.delete()
  >>> t.delete()
  
  """
  versioned_on = db.DateTimeProperty()
  dataset_key = db.Property()
  current_key = db.Property()
  
  @property
  def current(self):
    return db.get(current_key)
  
  @classmethod
  def make(cls, instance, time=datetime.datetime.now):
    """ Makes a copy of an instance object, does not put """
    if callable:
      time = time()
    versioned = cls(current_key=instance.key(), versioned_on=time)
    
    if hasattr(instance, 'dataset'):
      versioned.dataset_key = instance.dataset.key()
    elif hasattr(instance, 'dataset_key'):
      versioned.dataset_key = instance.dataset_key
    
    for name, prop in instance.properties().iteritems():
      value = prop.get_value_for_datastore(instance)
      value = value or None
      setattr(versioned, str('v__' + name), value)
    
    for name in instance.dynamic_properties():
      setattr(versioned, str('v__' + name), getattr(instance, name))
    
    return versioned
  
  def restore(self):
    """ Returns (unsaved) versioned object """
    current = self.current
    for name in self.dynamic_properties():
      setattr(current, name[3:], getattr(self, name))
    return current
